/**
 * Copyright (c) 2020-2021 ThunderSoft
 * All Rights Reserved by Thunder Software Technology Co., Ltd and its affiliates.
 * You may not use, copy, distribute, modify, transmit in any form this file
 * except in compliance with ThunderSoft in writing by applicable law.
 *
 */
#include <stdio.h>
#include <assert.h>
#include "lfs.h"

#include "bl_mtd.h"

#define MTD_DEVICE_OPEN_OK         (0)
#define SECTOR_SIZE_FLASH   4096
#define LFS_BLOCK_SIZE      SECTOR_SIZE_FLASH
#define LFS_READ_SIZE       8
#define LFS_PROG_SIZE       8
#define LFS_CACHE_SIZE      8
#define LFS_LOOKAHEAD_SIZE  8
#define LFS_BLOCK_CYCLES    (400)

#define LFS_BAK_FW_BLOCK_COUNT     (168)
#define LFS_FLASH_BEGIN     0x1E0000
#define LFS_FLASH_END       (LFS_FLASH_BEGIN + LFS_FLASH_SIZE)
#define LFS_FILE_NAME_MAX   (32)

#define OTA_LFS_FLASH_SIZE      (LFS_BAK_FW_BLOCK_COUNT * LFS_BLOCK_SIZE)
#define OTA_LFS_FLASH_BEGIN     0x138000
#define OTA_LFS_FLASH_END       (LFS_FLASH_BEGIN + LFS_FLASH_SIZE)
/**
 * ota_option
 * false: use media partition
 * true: use FW patition
 **/
static bool ota_option = false;
static bl_mtd_handle_t handle_fs = NULL;

static int ts_flash_read(const struct lfs_config* c, lfs_block_t block, lfs_off_t off, void* buffer, lfs_size_t size);

static int ts_flash_prog(const struct lfs_config* c, lfs_block_t block, lfs_off_t off, const void* buffer, lfs_size_t size);

static int ts_flash_erase(const struct lfs_config* c, lfs_block_t block);

static int ts_flash_sync(const struct lfs_config* c);

extern uint8_t hal_boot2_get_active_partition(void);
bool littlefs_get_otaopt() {
    printf("littlefs_get_otaopt opt is %d\r\n", ota_option);
    return ota_option;
}

void littlefs_set_otaopt(bool opt) {
    printf("littlefs_set_otaopt opt is %d\r\n", opt);
    ota_option = opt;
}

void littlefs_config_init(struct lfs_config *cfg)
{
    //int ret;
    bl_mtd_info_t mtd_info;

    cfg->read = ts_flash_read;
    cfg->prog = ts_flash_prog;
    cfg->erase = ts_flash_erase;
    cfg->sync = ts_flash_sync;

    // block device configuration
    cfg->read_size = LFS_READ_SIZE;
    cfg->prog_size = LFS_PROG_SIZE;
    cfg->block_size = LFS_BLOCK_SIZE;
    cfg->cache_size = LFS_CACHE_SIZE;
    cfg->lookahead_size = LFS_LOOKAHEAD_SIZE;
    cfg->block_cycles = LFS_BLOCK_CYCLES;
    cfg->name_max = LFS_FILE_NAME_MAX;

    if (false == ota_option) {
        // ota_option == false, operate the media partition
        if ((MTD_DEVICE_OPEN_OK != bl_mtd_open(BL_MTD_PARTITION_NAME_ROMFS, &handle_fs, BL_MTD_OPEN_FLAG_BUSADDR))) {
            printf("error when get romfs partition\r\n");
            return;
        }
    } else {
        printf("littlefs should not init within ota mode\r\n");
        assert(0);

        // ota_option == true, operate the ota partition
        cfg->block_count = LFS_BAK_FW_BLOCK_COUNT;
        int activeId = hal_boot2_get_active_partition();
        if (0 == activeId) {
            /* Now FW[0] is running, need write to FW[1] partition */
            if ((MTD_DEVICE_OPEN_OK != bl_mtd_open(BL_MTD_PARTITION_NAME_FW_DEFAULT, &handle_fs, BL_MTD_OPEN_FLAG_BACKUP))) {
                printf("error when get FW[1] ota partition\r\n");
                return;
            }
        } else {
            /* Now FW[1] is running, need write to FW[0] partition */
            if ((MTD_DEVICE_OPEN_OK != bl_mtd_open(BL_MTD_PARTITION_NAME_FW_DEFAULT, &handle_fs, BL_MTD_OPEN_FLAG_BUSADDR))) {
                printf("error when get FW[0] ota partition\r\n");
                return;
            }
        }
    }

    bl_mtd_info(handle_fs, &mtd_info);

    cfg->block_count = mtd_info.size / LFS_BLOCK_SIZE;

    printf("littlefs block count: %ld\r\n", cfg->block_count);
}

int ts_flash_read(const struct lfs_config* c, lfs_block_t block, lfs_off_t off, void* buffer, lfs_size_t size)
{
    unsigned int addr = block * LFS_BLOCK_SIZE + off;
    assert(c != NULL);
    assert(buffer != NULL);

    int ret = 0;
    if (!handle_fs)
    {
        printf("handle_fs is NULL\r\n");
        return -1;
    }

    if (block >= c->block_count)
    {
        printf("addr %d is invalid\r\n", (int)(addr));
        return -1;
    }

    ret = bl_mtd_read(handle_fs, addr, (unsigned int)size, (uint8_t *)buffer);

    return ret;
}

int ts_flash_prog(const struct lfs_config* c, lfs_block_t block, lfs_off_t off, const void* buffer, lfs_size_t size)
{
    unsigned int addr = block * LFS_BLOCK_SIZE + off;
    assert(c != NULL);
    assert(buffer != NULL);

    if (!handle_fs)
    {
        return -1;
    }

    if (block >= c->block_count)
    {
        printf("addr %d is invalid\r\n", (int)(addr));
        return -1;
    }

    return bl_mtd_write(handle_fs, addr, (unsigned int)size, (const uint8_t *)buffer);
}

int ts_flash_erase(const struct lfs_config* c, lfs_block_t block)
{
    unsigned int addr = block * LFS_BLOCK_SIZE;
    assert(c != NULL);

    if (!handle_fs)
    {
        return -1;
    }

    if (block >= c->block_count)
    {
        printf("addr %d is invalid\r\n", (int)(addr));
        return -1;
    }

    return bl_mtd_erase(handle_fs, addr, (unsigned int)LFS_BLOCK_SIZE);
}

int ts_flash_sync(const struct lfs_config* c)
{
    return 0;
}

